package abc.converter;


import abc.utils.CollectionUtil;
import abc.utils.StringUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.shortconverter.ShortNumberConverter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author linlurui
 * @Description EasyExcel自定义LocalDateTime系列时间日期转换器
 * 用法：
 *      1、@ExcelProperty(value = {"添加时间"}, converter = BaseDateConverter.LocalDateTimeConverter.class)
 *      2、ExcelWriter writer = EasyExcel.write(response.getOutputStream()).registerConverter(new BaseDateConverter.LocalDateConverter()).build();
 * @Date 2022-08-31 19:23:08
 */
public class BaseExcelConverter {

    static {
        converts = new ArrayList<>() {{
            add(new LongConverter());
            add(new LocalDateConverter());
            add(new LocalTimeConverter());
            add(new LocalDateTimeConverter());
            add(new DateConverter());
            add(new SqlDateConverter());
            add(new BigDecimalConverter());
            add(new BooleanConverter());
            add(new ShortNumberConverter());
            add(new CellDataTypeEnumConverter());
            add(new StringConverter());
            add(new IntegerConverter());
            add(new FloatConverter());
            add(new DoubleConverter());
        }};
    }

    public final static List<Converter> converts;

    public static final Pattern PROPERTY_REGEXP = Pattern.compile("([\\u4e00-\\u9fa5\\w][\\u4e00-\\u9fa5\\w\\d_]*)\\s*[:：]\\s*(([,;]?\\s*([\\w_][\\w\\d_]*)\\s*[\\-=]\\s*([\\u4e00-\\u9fa5\\w][\\u4e00-\\u9fa5\\w\\d_]*)\\s*)+)\\s*");

    /**
     * 核心抽象类，负责不同类的数据类型装换
     * @param <T> LocalXXX类泛型
     */
    private static abstract class CoreConverter<T> implements Converter<T> {

        private Class<T> clazz;

        /**
         * 指定Class类型，接收LocalDate.class，LocalTime.class，LocalDateTime.class
         */
        public CoreConverter(Class<T> clazz) {
            this.clazz = clazz;
        }

        /**
         * 导入支持的数据类型
         */
        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        /**
         * 导出支持的数据类型
         */
        @Override
        public Class supportJavaTypeKey() {
            return clazz;
        }

        /**
         * 导入时，数据类型转换
         * @param cellData excel单元格数据
         * @param property 单元格样式
         * @param config 全局配置
         * @return
         */
        @Override
        public T convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty property, GlobalConfiguration config) throws Exception {
            // LocalDate日期转换
            if (cellData.getData() instanceof LocalDate) {
                return (T) LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));

                // LocalTime时间转换
            } else if (cellData.getData() instanceof LocalTime) {
                return (T) LocalTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("HH:mm:ss"));

                // LocalDateTime时间日期转换
            } else if (cellData.getData() instanceof LocalDateTime) {
                return (T) LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
            else if(cellData.getData() instanceof Date) {
                return (T) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format((Date) cellData.getData());
            }
            else if(cellData.getData() instanceof java.sql.Date) {
                return (T) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format((java.sql.Date) cellData.getData());
            }
            else if(cellData.getData() instanceof String) {
                return (T) getJavaData((String) cellData.getData(), property);
            }
            else if(cellData.getData() instanceof CellDataTypeEnum) {
                return (T) cellData.getData();
            }

            return (T) StringUtil.cast(clazz, cellData.getData().toString());
        }

        /**
         * 导出时，数据类型转换
         * @param obj 当前数据
         * @param property 单元格样式
         * @param config 全局配置
         * @return
         */
        @Override
        public WriteCellData<?> convertToExcelData(T obj, ExcelContentProperty property, GlobalConfiguration config) throws Exception {
            // LocalDate日期转换
            if (obj instanceof LocalDate) {
                return new WriteCellData<>(((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

                // LocalTime时间转换
            } else if (obj instanceof LocalTime) {
                return new WriteCellData<>(((LocalTime) obj).format(DateTimeFormatter.ofPattern("HH:mm:ss")));

                // LocalDateTime时间日期转换
            } else if (obj instanceof LocalDateTime) {
                return new WriteCellData<>(((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }
            else if(obj instanceof Date) {
                return new WriteCellData<>(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format((Date) obj));
            }
            else if(obj instanceof java.sql.Date) {
                return new WriteCellData<>(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format((java.sql.Date) obj));
            }
            else if(obj instanceof String) {
                return new WriteCellData<>(getExcelData((String) obj, property));
            }
            else if(obj instanceof Integer) {
                return new WriteCellData<>(getExcelData(obj.toString(), property));
            }
            else if(obj instanceof Boolean) {
                return new WriteCellData<>(getExcelData(obj.toString(), property));
            }
            else if(obj instanceof BigDecimal) {
                return new WriteCellData<>((BigDecimal) obj);
            }
            else if(obj instanceof Float) {
                return new WriteCellData<>((BigDecimal) obj);
            }
            else if(obj instanceof Double) {
                return new WriteCellData<>((BigDecimal) obj);
            }
            else if(obj instanceof CellDataTypeEnum) {
                return new WriteCellData<>(((CellDataTypeEnum) obj).name());
            }
            return new WriteCellData(obj.toString());
        }

        private String getExcelData(String value, ExcelContentProperty property) {
            ExcelProperty excelProperty = property.getField().getAnnotation(ExcelProperty.class);
            if(excelProperty == null ||
                excelProperty.value() == null ||
                excelProperty.value().length < 1) {
                return value;
            }

            String label = StringUtil.join("", List.of(excelProperty.value()));

            Matcher m = PROPERTY_REGEXP.matcher(label);

            if(m.find()) {
                List<List<String>> expList = StringUtil.splitString2List(m.group(2), "[,;，； ]").stream()
                        .map(a -> StringUtil.splitString2List(a, "[\\-=]")).toList();
                String finalValue = value;
                Optional<String> opt = expList.stream().filter(a -> a.get(0).equals(finalValue)).map(b -> b.get(1)).findFirst();
                if(opt.isPresent()) {
                    value = opt.get();
                }
            }
            else {
                if(Boolean.class.equals(property.getField().getType())) {
                    if(value.equals("true") || value.equals("1")) {
                        return "是";
                    }
                    else {
                        return "否";
                    }
                }
            }

            return value;
        }

        private <T> T getJavaData(String value, ExcelContentProperty property) {
            T result = (T) StringUtil.cast(clazz, value);
            ExcelProperty excelProperty = property.getField().getAnnotation(ExcelProperty.class);
            if(excelProperty == null ||
                    excelProperty.value() == null ||
                    excelProperty.value().length < 1) {
                return result;
            }

            String label = StringUtil.join("", List.of(excelProperty.value()));

            Matcher m = PROPERTY_REGEXP.matcher(label);

            if(m.find()) {
                List<List<String>> expList = StringUtil.splitString2List(m.group(2), "[,;，； ]").stream()
                        .map(a -> StringUtil.splitString2List(a, "[\\-=]")).toList();
                String finalValue = value;
                Optional<String> opt = expList.stream().filter(a -> a.get(1).equals(finalValue)).map(b -> b.get(0)).findFirst();
                if(opt.isPresent()) {
                    result = (T) StringUtil.cast(clazz, opt.get());
                }
            }
            else {
                if(Boolean.class.equals(property.getField().getType())) {
                    if(value.equals("允许") || value.equals("是") || value.equals("true") || value.equals("1")) {
                        return (T) Boolean.TRUE;
                    }
                    else {
                        return (T) Boolean.FALSE;
                    }
                }
            }

            return result;
        }

    }

    /**
     * LocalDate数据类型转换器
     */
    public static class LocalDateConverter extends CoreConverter<LocalDate> {
        public LocalDateConverter() {
            super(LocalDate.class);
        }
    }

    /**
     * LocalTime数据类型转换器
     */
    public static class LocalTimeConverter extends CoreConverter<LocalTime> {
        public LocalTimeConverter() {
            super(LocalTime.class);
        }
    }

    /**
     * LocalDateTime数据类型转换器
     */
    public static class LocalDateTimeConverter extends CoreConverter<LocalDateTime> {
        public LocalDateTimeConverter() {
            super(LocalDateTime.class);
        }
    }

    /**
     * Date数据类型转换器
     */
    public static class DateConverter extends CoreConverter<Date> {
        public DateConverter() {
            super(Date.class);
        }
    }

    /**
     * SqlDate数据类型转换器
     */
    public static class SqlDateConverter extends CoreConverter<java.sql.Date> {
        public SqlDateConverter() {
            super(java.sql.Date.class);
        }
    }

    /**
     * Date数据类型转换器
     */
    public static class StringConverter extends CoreConverter<String> {
        public StringConverter() {
            super(String.class);
        }
    }

    /**
     * BigDecimal数据类型转换器
     */
    public static class BigDecimalConverter extends CoreConverter<BigDecimal> {
        public BigDecimalConverter() {
            super(BigDecimal.class);
        }
    }

    /**
     * Boolean数据类型转换器
     */
    public static class BooleanConverter extends CoreConverter<Boolean> {
        public BooleanConverter() {
            super(Boolean.class);
        }
    }

    /**
     * Integer数据类型转换器
     */
    public static class IntegerConverter extends CoreConverter<Integer> {
        public IntegerConverter() {
            super(Integer.class);
        }
    }

    /**
     * Float数据类型转换器
     */
    public static class FloatConverter extends CoreConverter<Float> {
        public FloatConverter() {
            super(Float.class);
        }
    }

    /**
     * Double数据类型转换器
     */
    public static class DoubleConverter extends CoreConverter<Double> {
        public DoubleConverter() {
            super(Double.class);
        }
    }

    /**
     * Long数据类型转换器
     */
    public static class LongConverter extends CoreConverter<Long> {
        public LongConverter() {
            super(Long.class);
        }
    }

    /**
     * CellDataTypeEnum数据类型转换器
     */
    public static class CellDataTypeEnumConverter extends CoreConverter<CellDataTypeEnum> {
        public CellDataTypeEnumConverter() {
            super(CellDataTypeEnum.class);
        }
    }
}
